package com.banmao.greedysnake.vo;

import com.banmao.greedysnake.gamepanel.controller.GamePanelCcontroller;
import com.banmao.greedysnake.util.SpringContextUtil;
import javafx.scene.paint.Color;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 *  蛇
 */
@Slf4j
@Data
public class Snake {

    // 头
    //@Data
    public static class Head extends Body {
        //private Double x;
        //private Double y;
        //private Double size;

        private Color color = Constants.HEAD_COLOR;

        public Head(Double x, Double y, Double size) {
            this.setX(x);
            this.setY(y);
            this.setSize(size);
            this.setColor(Constants.HEAD_COLOR);
        }

    }
    // 身体
    @Data
    public static class Body {
        private Double x;
        private Double y;
        private Double size = Constants.POINT_SIZE;

        private Color color = Constants.BODY_COLOR;

        public Body() {

        }

    }
    // 方向
    public enum Direction {
        UP,
        DOWN,
        RIGHT,
        LEFT
    }

    private Head head;
    private List<Body> bodies = new ArrayList<>();
    private Integer length;

    private static final Double SIZE = Constants.POINT_SIZE;
    private static final Double STEP = SIZE;

    private Direction direction = Direction.RIGHT;
    private Direction nextDirection = Direction.RIGHT; // 下一步方向，用于判断方向是否变更

    private Food food; // 食物坐标
    private Boolean hasEat;
    private Integer score = 0; // 得分

    public Snake() {
        head = new Head(60., 60., SIZE);
        bodies.add(head);
        // 初始化生成两个 body
        Body body = generateNextBody();
        bodies.add(body);
        body = generateNextBody();
        bodies.add(body);

        food = Food.generateFood(this);
    }

    // 生成下一节身体
    private Body generateNextBody() {
        // 获取身体最后的一节
        Body preBody = bodies.get(bodies.size() - 1);

        Body nextBody = new Body();
        // 判断现在的方向
        switch (direction) {
            case UP:
                // 向上时，下一节身体的x不变，y增加preBody.size的一半和nextBody.size的一半
                nextBody.setX(preBody.getX());
                nextBody.setY(preBody.getY() + (preBody.getSize() / 2 + nextBody.getSize() / 2));
                break;
            case DOWN:
                // 向下时，下一节身体的x不变，y减少preBody.size的一半和nextBody.size的一半
                nextBody.setX(preBody.x);
                nextBody.setY(preBody.getY() - (preBody.size / 2 + nextBody.size / 2));
                break;
            case RIGHT:
                // 向右时，下一节身体的y不变，x减少preBody.size的一半和nextBody.size的一半
                nextBody.setY(preBody.getY());
                nextBody.setX(preBody.getX() - (preBody.getSize() / 2 + nextBody.getSize() / 2));
                break;
            case LEFT:
                // 向左时，下一节身体的y不变，x增加preBody.size的一半和nextBody.size的一半
                nextBody.setY(preBody.y);
                nextBody.setX(preBody.x + (preBody.size / 2 + nextBody.size / 2));
                break;
        }

        return nextBody;
    }

    // 移动
    public void move() {

        // 遍历bodies
        if(nextDirection != direction) {
            log.debug("snake改变方向, direction: {}, nextDirection: {}", direction, nextDirection);
            direction = nextDirection;
        }

        // 先生成头部
        generateHead();

        // 没必要让 bodies 的所有元素自己计算
        // 让头部后面的身体，每一节拿到前一节的坐标
        for (int i = bodies.size() - 1; i > 0; i--) {
            Body curBody = bodies.get(i);
            //Body preBody = bodies.get(i - 1);
            //
            //curBody.setX(preBody.getX());
            //curBody.setY(preBody.getY());
            curBody.setColor(Constants.BODY_COLOR);
        }

        //bodies.get(0).setColor(Constants.BODY_COLOR);


    }

    private void generateHead() {

        Body body = bodies.get(0); // 第一个身体
        head = new Head(body.getX(), body.getY(), SIZE);

        // 判断现在的方向
        switch (direction) {
            case UP -> {
                // 向上时，身体的x不变，y减少
                head.setY(body.getY() - STEP);
                if (head.getY() < 0) {
                    head.setY(Constants.CANCAS_HEIGHT - SIZE);
                }
            }
            case DOWN -> {
                // 向下时，身体的x不变，y减少
                head.setY(body.getY() + STEP);
                if (head.getY() >= Constants.CANCAS_HEIGHT) {
                    head.setY(0.);
                }
            }
            case RIGHT -> {
                // 向右时，身体的y不变，x增加
                head.setX(body.getX() + STEP);
                if (head.getX() >= Constants.CANCAS_WIDTH) {
                    head.setX(0.);
                }
            }
            case LEFT -> {
                // 向左时，身体的y不变，x减少
                head.setX(body.getX() - STEP);
                if (head.getX() < 0) {
                    head.setX(Constants.CANCAS_WIDTH - SIZE);
                }
            }
        }

        bodies.add(0, head);

        // 判断生成的头部与food是否重合
        boolean eatFood = checkEatFood();
        boolean crashBody = checkCrashBody();

        if(eatFood) {
            score++;
            log.info("吃到food了，score: {}", score);
            food = Food.generateFood(this);
        } else if(crashBody) {
            // 撞到body了，游戏失败
            log.info("游戏失败！");
            GamePanelCcontroller gameController = (GamePanelCcontroller) SpringContextUtil.getBean(GamePanelCcontroller.class.getSimpleName());
            gameController.setFail(true);
            gameController.pauseGame(null);
        } else {
            // 去除最后一个节点，如果吃到food，就不去除
            bodies.remove(bodies.size() - 1);
        }
    }

    // 判断是否吃到food
    private boolean checkEatFood() {
        Double headX = head.getX();
        Double headY = head.getY();

        Double foodX = food.getFoodX();
        Double foodY = food.getFoodY();

        return Double.compare(headX, foodX) == 0 && Double.compare(headY, foodY) == 0;
    }

    // 判断是否撞到body
    private boolean checkCrashBody() {
        Double headX = head.getX();
        Double headY = head.getY();

        for (Body body : bodies) {
            Double bodyX = body.getX();
            Double bodyY = body.getY();

            if(body != head) {
                if(Double.compare(headX, bodyX) == 0 && Double.compare(headY, bodyY) == 0) {
                    return true;
                }
            }
        }

        return false;
    }


}
